package com.gitee.liuzhi.demo.lambda;

import com.gitee.liuzhi.common.model.Student;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Created by 2024/7/31/0031
 *
 * @author LiuZhi
 * @version V1.0
 * @description: Lambda表达式
 * <p>
 * Lambda 概述
 * 1. Lambda 是 JDK8 的一个语法糖，它可以对某些匿名内部类的写法进行简化，它是函数式编程思想的一个重要体现
 * 2. Lambda 没有名称，但它有参数列表、函数主体、返回类型，可能还有一个可以抛出的异常列表
 * 3. lambda表达式会有延迟执行的效果，当条件成立后才会执行，可以提高性能
 * <p>
 * Lambda 基本格式
 * (参数类型 参数名) -> { 代码 }
 * ()  中的参数列表，没有参数，就空着，有多个参数就用逗号隔开
 * ->  代表传递的意思，把参数传递给方法体
 * { }  重写接口的抽象方法
 * 如：(Integer v1, Integer v2) -> v1.compareTo(v2);
 * <p>
 * Lambda 省略格式
 * 1. 小括号里的参数类型可以不写
 * 2. 如果小括号里有且仅有一个参数，则小括号也可以不写
 * 3. 如果大括号里有且仅有一条语句，则无论是否有返回值，都可以省略大括号，return 关键字和分号(要省略大括号，return和分号必须一起省略)
 * <p>
 * Lambda 几种有效表达式 {@link LambdaDemo#testLambdaFormat()}
 * <p>
 * Lambda 类型检查 {@link LambdaDemo#testLambdaTypeCheck()}
 * 1. Lambda的类型是从使用Lambda的上下文推断出来的；上下文（比如，接受它传递的方法的参数，或接受它的值的局部变量）中Lambda表达式需要的类型称为目标类型
 * 2. 同一个Lambda表达式可以与不同的函数式接口联系来，只要它们的抽象方法签名能够兼容
 * <p>
 * Lambda 使用局部变量
 * 1. Lambda允许使用自由变量（外层作用域中定义的变量），包括实例变量和静态变量
 * 2. Lambda使用局部变量必须显示声明为final或事实上是final
 * 2.1 在Java线程模型中，栈帧中的局部变量是线程私有的，永远不需要同步
 * 2.2 我们可以把Lambda表达式看作是一个匿名内部类实例化出来的对象，在Lambda中引用的局部变量，其实是Lambda式中隐式的创建了一个同名的变量，将局部变量的值copy了一份
 * 因此它们是两个不同的符号，同时Java为了防止我们在Lambda中修改其隐式创建出来的变量，就规定这种变量必须是final或等效于final的
 * <p>
 * Lambda 方法引用 {@link LambdaDemo#testMethodReference}
 * 1. 如果一个Lambda代表的只是“直接调用这个方法”，那最好还是用名称来调用它，而不是去描述如何调用它（语法糖，表达同样的事情时要写的代码更少了）
 * 2. 目标引用放在分隔符 :: 前，方法的名称放在后面，例如，Apple::getWeight就是引用了Apple类中定义的方法getWeight
 * <p>
 * Lambda 复合表达式 {@link LambdaDemo#testLambdaComposite()}
 * 1. 许多函数式接口，比如用于传递Lambda表达式的Comparator、Function和Predicate都提供了允许你进行复合的方法
 * 2. 这意味着你可以把多个简单的Lambda复合成复杂的表达式
 */
public class LambdaDemo {

    public static void main(String[] args) {
        testLambdaFormat();
        testLambdaTypeCheck();
        testMethodReference();
        testLambdaComposite();
    }

    public static void testLambdaFormat() {
        // 第一种：接受一个类型，返回一个类型，Lambda没有return语句，因为已经隐含了return
        Function<String, Integer> function = (String str) -> str.length();
        System.out.println(function.apply("Hello World!!!"));
        Function<Integer, Boolean> function1 = (Integer i) -> i % 2 == 0;
        System.out.println(function1.apply(3));
        // 第二种：接受一个类型，没有返回值
        Consumer<Integer> consumer = (Integer i) -> System.out.println(i);
        consumer.accept(333);
        // 第三种：没有参数，但是有返回值
        Supplier<Integer> supplier = () -> new Random().nextInt();
        System.out.println(supplier.get());
        // 第四种：没有参数，也没有返回值
        Runnable runnable = () -> System.out.println("Hello World!!!");
        runnable.run();
    }

    private static void testLambdaTypeCheck() {
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student(1, "111", 18));
        studentList.add(new Student(2, "222", 19));
        studentList.add(new Student(3, "333", 18));
        // 1. 使用Lambda的上下文是filterStudent()
        // 2. 目前类型是Predicate<T>，于是将Student与T绑定
        // 3. Predicate<Student>的抽象方法是test
        // 4. test方法接受一个Student，并返回一个boolean；Student -> boolean
        // 5. 函数描述符Student -> boolean匹配Lambda的签名，它接受一个Student返回一个boolean，因此代码类型检查无误
        List<Student> filterList = filterStudent(studentList, (student -> student.getAge().equals(18)));
    }

    private static void testMethodReference() {
        // 方法引用
        Function<String, Integer> function = String::length;
        Integer length = function.apply("111");
        Function<String, Integer> function1 = Integer::parseInt;
        Integer apply = function1.apply("123");
        // 构造方法引用
        Supplier<Student> studentSupplier = Student::new;
        Student student = studentSupplier.get();
    }

    private static void testLambdaComposite() {
        // 比较器复合 Comparator.comparing
        List<Student> list = new ArrayList<>();
        list.sort(Comparator.comparing(Student::getAge)
                .reversed()
                .thenComparing(Student::getName));
        // 谓词复合 Predicate接口包括了negate、and和or，可以使用Predicate来创建复杂的谓词
        Student student = new Student();
        student.setName("111");
        student.setAge(2);
        Predicate<Student> agePredicate = s -> s.getAge() > 1;
        Predicate<Student> namePredicate = s -> "111".equals(s.getName());
        Predicate<Student> and = agePredicate.and(namePredicate);
        boolean test = and.test(student);
        System.out.println(test);

        // 函数复合 Function接口为此配了andThen和compose两个默认方法，它们都会返回Function的一个实例
        Function<Integer, Integer> f = x -> x + 1;
        Function<Integer, Integer> g = x -> x * 2;
        // andThen => g(f(x))
        Function<Integer, Integer> h = f.andThen(g);
        System.out.println(h.apply(1));
        // compose => f(g(x))
        Function<Integer, Integer> i = f.compose(g);
        System.out.println(i.apply(1));

    }

    public static <T> List<T> filterStudent(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T t : list) {
            if (predicate.test(t)) {
                result.add(t);
            }
        }
        return result;
    }

}
